<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Auth_model extends CI_Model {

	protected $iterations = 1000;

	function __construct()
	{
		parent::__construct();
		$this->htpass = BASEPATH.'../application/.htpasswd';
		$this->_auth();
	}

	// поддерживается только apr1 md5
	private function _auth ()
	{
		isset($_SERVER['PHP_AUTH_USER'])?$user=$_SERVER['PHP_AUTH_USER']:$user='';
		isset($_SERVER['PHP_AUTH_PW'])?$pass=$_SERVER['PHP_AUTH_PW']:$pass='';
		if ($user == '' || $pass == '') {
			$this->_error();
			return false;
		}
		if ($fp=fopen($this->htpass,'r'))
		{
			while ($line=fgets($fp))
			{
				list($fuser,$fpass)=explode(':', trim($line));
				if ($fuser == $user)
				{
					if ($this->verify($pass, $fpass))
					{
						return true;
					}
					else
					{
						$this->_error();
					}
				}
			}

		}
		$this->_error();
	}

	private function _error ()
	{
		header('WWW-Authenticate: Basic realm="My Realm"');
		header('HTTP/1.0 401 Unauthorized');
		show_error('Доступ запрещен.', '401');
		exit;
	}

    public function verify($password, $hash) {
        $bits = explode('$', $hash);
        if (!isset($bits[3]) || $bits[1] != 'apr1') {
            return false;
        }
        $test = $this->hash($password, $bits[2], $this->iterations);
        return $test == $hash;
    }

	protected function hash($password, $salt, $iterations) {
		$len  = strlen($password);
		$text = $password . '$apr1$' . $salt;
		$bin  = md5($password.$salt.$password, true);
		for ($i = $len; $i > 0; $i -= 16) {
			$text .= substr($bin, 0, min(16, $i));
		}
		for ($i = $len; $i > 0; $i >>= 1) {
			$text .= ($i & 1) ? chr(0) : $password[0];
		}
		$bin = $this->iterate($text, $iterations, $salt, $password);
		return $this->convertToHash($bin, $salt);
	}

	protected function iterate($text, $iterations, $salt, $password) {
		$bin = md5($text, true);
		for ($i = 0; $i < $iterations; $i++) {
			$new = ($i & 1) ? $password : $bin;
			if ($i % 3) {
				$new .= $salt;
			}
			if ($i % 7) {
				$new .= $password;
			}
			$new .= ($i & 1) ? $bin : $password;
			$bin  = md5($new, true);
		}
		return $bin;
	}

	protected function convertToHash($bin, $salt) {
		$tmp  = '$apr1$'.$salt.'$';
		$tmp .= $this->to64(
			(ord($bin[0])<<16) | (ord($bin[6])<<8) | ord($bin[12]),
			4
		);
		$tmp .= $this->to64(
			(ord($bin[1])<<16) | (ord($bin[7])<<8) | ord($bin[13]),
			4
		);
		$tmp .= $this->to64(
			(ord($bin[2])<<16) | (ord($bin[8])<<8) | ord($bin[14]),
			4
		);
		$tmp .= $this->to64(
			(ord($bin[3])<<16) | (ord($bin[9])<<8) | ord($bin[15]),
			4
		);
		$tmp .= $this->to64(
			(ord($bin[4])<<16) | (ord($bin[10])<<8) | ord($bin[5]),
			4
		);
		$tmp .= $this->to64(
			ord($bin[11]),
			2
		);
		return $tmp;
	}

	protected function to64($num, $size) {
        static $seed = '';
        if (empty($seed)) {
            $seed = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
                    'abcdefghijklmnopqrstuvwxyz';
        }
        $result = '';
        while (--$size >= 0) {
            $result .= $seed[$num & 0x3f];
            $num   >>= 6;
        }
        return $result;
    }

}